﻿/*
(c) 2010 - Open Source Business Foundation e. V. (www.osbf.de)
This software is provided under Microsoft Reciprocal License (Ms-RL)
(http://www.microsoft.com/opensource/licenses.mspx#Ms-RL)
All other rights reserved
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure.ServiceRuntime;

namespace MicroFormatProcessing
{
    /// <summary>
    /// This class is doing the Query part for the Microformats 
    /// </summary>
    public class MicroFormatRepository
    {
        private static CloudStorageAccount storageAccount;
        private MicroFormatContext context;

        static  MicroFormatRepository()
        {
            CloudStorageAccount.SetConfigurationSettingPublisher(
                     (configName, configSetter) =>
                                  configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)));
            storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
            storageAccount.CreateCloudTableClient().CreateTableIfNotExist(MicroFormatContext.TABLE_NAME);
            // CloudTableClient.CreateTablesFromModel(typeof(MfDefinitionContext), storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
        }

        public MicroFormatRepository()
        {
            this.context = new MicroFormatContext(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
            this.context.RetryPolicy = RetryPolicies.Retry(3, TimeSpan.FromSeconds(1));
            this.context.IgnoreResourceNotFoundException = true;
        }

        public List<MicroFormat> FindByUserId(string userId)
        {
            return FindByUserIdAndMfId(userId, null);
        }


        public List<MicroFormat> FindByUserIdAndMfId(string userId, string mfId)
        {
            List<MicroFormat> result = new List<MicroFormat>();
            HashSet<string> references = new HashSet<string>();
            if (this.context.MicroFormats.FirstOrDefault() != null)
            {
                CloudTableQuery<MicroFormat> query;
                if (mfId != null)
                {
                    query = (from mf in this.context.MicroFormats where mf.PartitionKey == userId && mf.RowKey == mfId select mf).AsTableServiceQuery<MicroFormat>();
                }
                else
                {
                    query = (from mf in this.context.MicroFormats where mf.PartitionKey == userId select mf).AsTableServiceQuery<MicroFormat>();
                }
                foreach (MicroFormat microFormat in query.Execute())
                {
                    microFormat.DeSerializeAttributes();
                    if (microFormat.GetReferenceCount() > 0)
                    {
                        foreach (string reference in microFormat.GetReferenceArray())
                        {
                            references.Add(reference);
                        }
                    }
                    else
                    {
                        result.Add(microFormat);
                    }
                }
                FindByUserIdAndReference(result, userId, references);
            }
            return result;
        }

        private void FindByUserIdAndReference(List<MicroFormat> result, string userId, HashSet<string> references)
        {
            if ((this.context.MicroFormats.FirstOrDefault() != null) && (references != null) && (references.Count > 0))
            {
                CloudTableQuery<MicroFormat> query = (from mf in this.context.MicroFormats where mf.PartitionKey == userId select mf).AsTableServiceQuery<MicroFormat>();
                foreach (MicroFormat microFormat in query.Execute())
                {
                    if (references.Contains(microFormat.RowKey))
                    {
                        microFormat.DeSerializeAttributes();
                        result.Add(microFormat);
                    }
                }
            }
        }

        // add a new MicroFormat to the table
        // if there already is a MicroFormat with the same PropertyHash (which is the RowKey)
        // change the old MicroFormat to a ReferenceHolder and add References to that
        public void Add(MicroFormat microFormat)
        {
            if (microFormat != null)
            {
                microFormat.SerializeAttributes();
                if (this.context.MicroFormats.FirstOrDefault() != null)
                {
                    MicroFormat referenceHolder = null;
                    CloudTableQuery<MicroFormat> query = (from mf in this.context.MicroFormats where mf.PartitionKey == microFormat.PartitionKey && mf.RowKey == microFormat.RowKey select mf).AsTableServiceQuery<MicroFormat>();
                    foreach (MicroFormat oldMf in query.Execute())
                    {
                        referenceHolder = oldMf;
                    }
                    if (referenceHolder != null)
                    {
                        referenceHolder.DeSerializeAttributes();
                        int maxIndex = 0;
                        foreach (string reference in referenceHolder.GetReferenceArray())
                        {
                            int separatorPos = reference.LastIndexOf('_');
                            if ((separatorPos >= 0) && (separatorPos < reference.Length - 1))
                            {
                                int index = Int32.Parse(reference.Substring(separatorPos + 1));
                                if (index > maxIndex)
                                {
                                    maxIndex = index;
                                }
                            }
                        }
                        if (maxIndex == 0)
                        {
                            MicroFormat referenceHolderCopy = new MicroFormat();
                            referenceHolderCopy.Init(referenceHolder);
                            referenceHolderCopy.SetUserId(referenceHolder.GetUserId());
                            referenceHolderCopy.RowKey = String.Format("{0}_{1}", referenceHolder.RowKey, 1);
                            this.context.Add(referenceHolderCopy);
                            referenceHolder.Clear();
                            referenceHolder.FoundOnDate = DateTime.UtcNow;
                            referenceHolder.AddReference(referenceHolderCopy.RowKey);
                            maxIndex = 1;
                        }
                        microFormat.RowKey = String.Format("{0}_{1}", microFormat.RowKey, maxIndex + 1);
                        referenceHolder.AddReference(microFormat.RowKey);
                        referenceHolder.SerializeAttributes();
                        this.context.Update(referenceHolder);
                    }
                }
                this.context.Add(microFormat);
                this.context.SaveChanges();
            }
        }

        public void Update(MicroFormat microFormat, bool attach)
        {
            if (microFormat != null)
            {
                microFormat.SerializeAttributes();
                if (attach)
                {
                    this.context.AttachTo(microFormat);
                }
                this.context.Update(microFormat);
                this.context.SaveChanges();
            }
        }

        public void Delete(MicroFormat microFormat, bool attach)
        {
            if (microFormat != null)
            {
                microFormat.SerializeAttributes();
                if (attach)
                {
                    this.context.AttachTo(microFormat);
                }
                this.context.Delete(microFormat);
                this.context.SaveChanges();
            }
        }
    }
}